package lionel.MyJMRI.MyTrain;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.net.URISyntaxException;


import javax.swing.JComponent;
import javax.swing.Timer;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.ProcessingInstruction;

import jmri.DccLocoAddress;
import jmri.DccThrottle;
import jmri.ThrottleListener;
import jmri.jmrit.XmlFile;
import jmri.jmrit.throttle.AddressPanel;
import jmri.jmrit.throttle.ControlPanel;
import jmri.jmrit.throttle.FunctionPanel;

import jmri.jmrit.throttle.WindowPreferences;

import lionel.MyBrowser.MyConductor;

public class MyTrain extends MyLocomotive implements MyConductor, ThrottleListener
{	
	private static final long serialVersionUID = -5499204235453587775L;

	static org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(MyTrain.class.getName());

	static final float CRUISE_SPEED = 0.6f ;
	static final float SLOW_SPEED = 0.2f ;
	static final float ESTOP_SPEED = -0.5f ;
	static final float STOP_SPEED = 0f ;
	static final float MAX_SPEED = 1f ;

	private static final int SPEED_SET_INTERVAL = 50 ;
	private static final int THROTTLE_CHECK_INTERVAL = 500;

	private DccThrottle m_throttle = null; 

	private Timer speedSetTimer = null;
	private SpeedSetTask speedSetTask = null;

	private Timer checkThrottleTimer = null;
	private CheckThrottleTask checkThrottleTask = null;
	private int checkThrottleTic = 0;

	private boolean mOnListen = false;

	private ControlPanel controlPanel;
	private FunctionPanel functionPanel;
	private AddressPanel addressPanel; // required for roster access

	public MyTrain(String id) 
	{
		super(id);
		speedSetTask = new SpeedSetTask();
		speedSetTimer = new Timer( SPEED_SET_INTERVAL, speedSetTask );
		checkThrottleTask = new CheckThrottleTask();
		checkThrottleTimer = new Timer( THROTTLE_CHECK_INTERVAL, checkThrottleTask);
		m_throttle = null;
		initGUI();
	}

	public MyTrain(MyLocomotive myLoc) 
	{ 
		super(myLoc); 
		speedSetTask = new SpeedSetTask();
		speedSetTimer = new Timer( SPEED_SET_INTERVAL, speedSetTask );
		checkThrottleTask = new CheckThrottleTask();
		checkThrottleTimer = new Timer( THROTTLE_CHECK_INTERVAL, checkThrottleTask);
		m_throttle = null;
		initGUI();
	}

	private class SpeedSetTask implements ActionListener {
		static final float SPEED_INCREMENT = 0.01f ;
		public int WkDir;

		public void actionPerformed(ActionEvent e) {
			float sp = m_throttle.getSpeedSetting() + WkDir * SPEED_INCREMENT;
			if (sp<0) sp = 0;
			if (sp>1) sp = 1;
			m_throttle.setSpeedSetting( sp );
			controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", sp, sp) );
		}
	}

	private class CheckThrottleTask implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			checkThrottleTic++;
			if (checkThrottle())
				checkThrottleTimer.stop();
		}
	}

	public String toString()
	{
		if ((mID != null) && (checkThrottle()))
			return(mID + " / " + m_throttle.toString() );
		if (mLocoAddress!=null)
			return(mID + " "+mLocoAddress+" throttle not available");
		return("WEIRD! Not even a roster, but train???");
	}

	public synchronized void locAccStop() {
		log.debug("Stopping acceleration task");
		if (speedSetTimer!=null)
			if (speedSetTimer.isRunning())
				speedSetTimer.stop();
	} 

	public synchronized void locAcc(boolean yn) {
		log.debug("Starting acceleration task");
		speedSetTask.WkDir = yn?1:-1;
		if ( checkThrottle() && (!speedSetTimer.isRunning()) )
			speedSetTimer.start();  		
	}

	public synchronized void locSwitchDir() {
		log.debug("locSwitchDir");
		if ( checkThrottle() ) { locDir( ! m_throttle.getIsForward() ); }
	}

	public synchronized void locDir(boolean plus) {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setIsForward( plus ); controlPanel.propertyChange( new PropertyChangeEvent(this, "IsForward", plus, plus) ); } 
	}

	public synchronized void locSwitchLight() {
		if ( checkThrottle() ) { m_throttle.setF0(! m_throttle.getF0() ) ; } 
	}

	public synchronized void locLight(boolean on) {
		if ( checkThrottle() ) { m_throttle.setF0(on); } 
	}

	public synchronized void locEStop() {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(ESTOP_SPEED); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", ESTOP_SPEED, ESTOP_SPEED) );}
	}

	public synchronized void locSetSpeed(float speed) {
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting( speed ); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", speed, speed) );}
	}

	public synchronized float locGetSpeed() {
		float speed = 0;
		if ( checkThrottle() ) { speed = m_throttle.getSpeedSetting(); }
		return speed;
	}

	public synchronized void locStop() {
		log.debug("locStop()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(STOP_SPEED); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", STOP_SPEED, STOP_SPEED) );}
	}

	public synchronized void locSlow() {
		log.debug("locSlow()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(SLOW_SPEED); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", SLOW_SPEED, SLOW_SPEED) );}
	}  

	public synchronized void locCruise() {
		log.debug("locCruise()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(CRUISE_SPEED); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", CRUISE_SPEED, CRUISE_SPEED) );}
	}

	public synchronized void locMax() {
		log.debug("locMax()");
		locAccStop();
		if ( checkThrottle() ) { m_throttle.setSpeedSetting(MAX_SPEED); controlPanel.propertyChange( new PropertyChangeEvent(this, "SpeedSetting", MAX_SPEED, MAX_SPEED) );}
	}

	private synchronized boolean checkThrottle()
	{
		if (m_throttle != null)
			return true;

		if (checkThrottleTic == 2)
		{
			try {
				image = loadImage( (getClass().getResource("NoThrottle.jpg")).toURI() );
			} catch (URISyntaxException e) {
				e.printStackTrace();
			}
			refresh();
		}
		return false;
	}

	public synchronized void requestThrottle()
	{
		if (mOnListen == false)
		{
			//			loadSVGs();
			log.debug("Requesting throttle for "+mLocoAddress);
			if ( jmri.InstanceManager.throttleManagerInstance().requestThrottle( ((DccLocoAddress)mLocoAddress).getNumber(), this) == false )
			{
				log.error("Throttle request refused by system");
				return ;
			}
			checkThrottleTic = 0;
			checkThrottleTimer.start();
			mOnListen = true;
			//			loadImage();
		}		
	}

	public synchronized void releaseThrottle()
	{
		savePrefs();
		locAccStop();
		locStop();
		locLight(false);
		if (checkThrottleTimer.isRunning())
		{
			log.debug("Canceling throttle request for "+mLocoAddress);
			jmri.InstanceManager.throttleManagerInstance().cancelThrottleRequest( ((DccLocoAddress)mLocoAddress).getNumber(), this);
			checkThrottleTimer.stop();
		}
		if (m_throttle != null)
		{	
			log.debug("Releasing throttle for "+mLocoAddress);
			addressPanel.notifyThrottleDisposed();
			controlPanel.notifyThrottleDisposed();
			functionPanel.notifyThrottleDisposed();
			m_throttle.release();
			m_throttle = null;
		}
		controlPanel.destroy();
		functionPanel.destroy();
		addressPanel.destroy();
	}

	public synchronized void notifyThrottleFound(DccThrottle t) {
		log.debug("Got throttle "+t+" for "+mLocoAddress);
		m_throttle = t;
		addressPanel.notifyThrottleFound(t);
		controlPanel.notifyThrottleFound(t);
		functionPanel.notifyThrottleFound(t);		
		mOnListen = false;
		checkThrottleTic = 0;
		if (checkThrottleTimer.isRunning() )
			checkThrottleTimer.stop();
		locLight(true);
		locDir(true);
	}
	
	private void setOpaque(JComponent jcomp)
	{
		jcomp.setOpaque(false);
		Component[] comps = jcomp.getComponents();
		for (int i=0; i<comps.length; i++)
		{
			try
			{
				JComponent jcmp2 = (JComponent) comps[i];
				setOpaque( jcmp2 );
			}
			catch(Exception e)
			{
			}
		}
	}

	private synchronized void initGUI()
	{
		log.debug("initGUI() start");

		controlPanel = new ControlPanel();
		controlPanel.setResizable(true);
		controlPanel.setClosable(true);
		controlPanel.setIconifiable(true);
		controlPanel.setTitle("Control Panel");
		controlPanel.pack();
		setOpaque(controlPanel);
		controlPanel.setVisible(true);
		controlPanel.setEnabled(true);

		functionPanel = new FunctionPanel();
		functionPanel.setResizable(true);
		functionPanel.setClosable(true);
		functionPanel.setIconifiable(true);
		functionPanel.setTitle("Function Panel");
		functionPanel.setSize(192, 260);
		functionPanel.setLocation(controlPanel.getWidth(), 0);
		setOpaque(functionPanel);
		functionPanel.setVisible(true);
		functionPanel.setEnabled(true);

		addressPanel = new AddressPanel();
		addressPanel.setResizable(true);
		addressPanel.setClosable(true);
		addressPanel.setIconifiable(true);
		addressPanel.setTitle("Address Panel");
		addressPanel.pack();
		addressPanel.setLocation(controlPanel.getWidth(), functionPanel.getHeight());
		addressPanel.setVisible(false);
		addressPanel.setRosterEntry(mRoster);

		functionPanel.setAddressPanel(addressPanel); // so the function panel can get access to the roster

		if (controlPanel.getHeight() < functionPanel.getHeight() + addressPanel.getHeight())
		{controlPanel.setSize(controlPanel.getWidth(),functionPanel.getHeight() + addressPanel.getHeight());}
		if (controlPanel.getHeight() > functionPanel.getHeight() + addressPanel.getHeight())
		{addressPanel.setSize(addressPanel.getWidth(),controlPanel.getHeight()-functionPanel.getHeight());}
		if (functionPanel.getWidth() < addressPanel.getWidth())
		{functionPanel.setSize(addressPanel.getWidth(),functionPanel.getHeight());}
		
		loadPrefs();
		this.add(controlPanel);
		this.add(functionPanel);
	}

	class MyTrainPrefs extends XmlFile {
	}

	protected void loadPrefs()
	{
		try {
			String imagePath = PATH_BASE + mID.trim() + "/prefs.xml";

			File file = new File(imagePath);
			MyTrainPrefs prefs = new MyTrainPrefs();
			Element root = prefs.rootFromFile(file);
			setXml(root.getChild("ThrottleFrame"));
		} catch (org.jdom.JDOMException ex) {
			log.warn("Loading trainPref exception:" + ex);
			return ;
		} catch (java.io.IOException ex) {
			log.warn("Loading trainPref exception:" + ex);
			return ;
		}
	}
	
	protected void savePrefs()
	{
		try {
			String imagePath = PATH_BASE + mID.trim() + "/prefs.xml";

			File file = new File(imagePath);
			MyTrainPrefs prefs = new MyTrainPrefs();
			Element root = new Element("throttle-config");
			Document doc = XmlFile.newDocument(root, XmlFile.dtdLocation+"throttle-config.dtd");
			java.util.Map m = new java.util.HashMap();
			m.put("type", "text/xsl");
			m.put("href", jmri.jmrit.XmlFile.xsltLocation+"throttle.xsl");
			ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m);
			doc.addContent(0,p);
			Element throttleElement = getXml();
			// don't save the loco address or consist address
//			throttleElement.getChild("AddressPanel").removeChild("locoaddress");
//			throttleElement.getChild("AddressPanel").removeChild("locoaddress");
			root.setContent(throttleElement);
			prefs.writeXML(file, doc);
			
		} catch (org.jdom.JDOMException ex) {
			log.warn("Saving trainPref exception:" + ex);
			return ;
		} catch (java.io.IOException ex) {
			log.warn("Saving trainPref exception:" + ex);
			return ;
		}
	}
	
	public void setXml(Element e)
	{
		int bSize = 23;
//		Element window = e.getChild("window");
//		WindowPreferences wp = new WindowPreferences();
//		wp.setPreferences(this, window);
		Element controlPanelElement = e.getChild("ControlPanel");
		controlPanel.setXml(controlPanelElement);
		if (((javax.swing.plaf.basic.BasicInternalFrameUI) controlPanel.getUI()).getNorthPane() != null)
			((javax.swing.plaf.basic.BasicInternalFrameUI) controlPanel.getUI()).getNorthPane().setPreferredSize( new Dimension(0,bSize));
		Element functionPanelElement = e.getChild("FunctionPanel");
		functionPanel.setXml(functionPanelElement);
		if (((javax.swing.plaf.basic.BasicInternalFrameUI) controlPanel.getUI()).getNorthPane() != null)
			((javax.swing.plaf.basic.BasicInternalFrameUI) functionPanel.getUI()).getNorthPane().setPreferredSize( new Dimension(0,bSize));
//		Element addressPanelElement = e.getChild("AddressPanel");
//		addressPanel.setXml(addressPanelElement);
//		if (((javax.swing.plaf.basic.BasicInternalFrameUI) controlPanel.getUI()).getNorthPane() != null)
//			((javax.swing.plaf.basic.BasicInternalFrameUI) addressPanel.getUI()).getNorthPane().setPreferredSize( new Dimension(0,bSize));
	}

	public Element getXml()
	{
		Element me = new Element("ThrottleFrame");
		me.setAttribute("title", this.ID() );

		java.util.ArrayList children =  new java.util.ArrayList(1);
		WindowPreferences wp = new WindowPreferences();

		children.add(wp.getPreferences(this));
		children.add(controlPanel.getXml());
		children.add(functionPanel.getXml());
//		children.add(addressPanel.getXml());
		me.setContent(children);
		return me;
	}
}
